window.djp||(djp={});
/**
 * создает объект, для работы со значениями, получаемыми асинхронно.
 * @return {Deferred}
 */
new function(){
  /**
   * передает наблюдателям полученное значение.
   * @param {Deferred} self
   */
  var _fire = function(self){
    var chain = self.chain,
    results = self.results,
    chainLength = chain.length,
    currentIndex = 0,
    subWatcher = null,
    item,
    nextPhaseChain = self.nextPhaseChain;
    /* если в очереди  закончились наблюдатели, начинаем новую фазу. */
    if (!chainLength && nextPhaseChain.length) {
      chainLength = (chain = nextPhaseChain).length;
      nextPhaseChain = self.nextPhaseChain = [];
    }
    while (chainLength > currentIndex && !self.paused){
      item = chain[currentIndex++];
      /* сохраняем цепочку команд для следующей фазы. */
      if (!item[3]) nextPhaseChain[nextPhaseChain.length] = item;
      /* если есть в цепочке наблюдатель за значением/ошибкой */
      if (item = item[results[1]]){
        /* если наблюдатель вернул результат: */
        if ( typeof(item = item(results[0], results[1])) != 'undefined'){
          /* получаемый вложенным асинхронным запросом */
          if (item.constructor != Array){
            if (results[1]==2){
              /* если идет процесс отмены, запускаем его во вложенных */
              item.cancel()
            } else {
              subWatcher = function(res, isErr){
                if(--self.paused==0){
                  self.results = [res, isErr];
                  _fire(self)
                }
              }
              /* будем ждать вложенный асинхронный результат */
              self.paused++
            }
          /* иначе этот результат изменяет текущий */
          } else {
            results = item
          }
        }
      }
    }
    self.results = results;
    subWatcher && self.paused && item.addWatchers(subWatcher, subWatcher, null, 1);
    /* удалем, отработавших наблюдатей, в текущей цепочке. */
    self.chain = chain.slice(0, currentIndex-1);
  },
  _part1 = function(self, res, isErr){
    if (self.results){
      if (self.chain.length){
        self.cancel()
      } else {
        self.results = null
      }
    }
    self.results = [res, isErr];
    (self.chain.length || self.nextPhaseChain.length) && _fire(self)
  },
  k,
  o = {
    setValue: function(res){
      _part1(this, res, 0)
    },
    setError: function(res){
      _part1(this, res, 1)
    },
    cancel: function(){
      var self = this;
      if (self.chain.length || self.nextPhaseChain.length) {
        self.results = [null,2];
        _fire(self)
      } else if (self.results){
        self.results = null
      }
    },
    /**
     * Подключает наблюдателей за полученным асинхронно значением.
     * @param {Function} onResultCallback получает результат и флаг ошибки.<br>если надо изменить результат<br>функция возвращает [result:Object, isError:Boolean]
     * @param {Function} onErrorCallback получает результат и флаг ошибки.<br>если надо изменить результат<br>функция возвращает [result:Object, isError:Boolean]
     * @param {Function} onCancelCallback получает результат и флаг процесса отмены.
     * @param {Boolean} toOnePhase не запоминать в цепочке команд,<br>получения значения, на следующей фазе.
     * @return {Deferred}
     */
    addWatchers: function( onResultCallback, onErrorCallback, onCancelCallback, toOnePhase ){
      var self = this,
      chain = self.chain,
      a = [onResultCallback, onErrorCallback, onCancelCallback];
      if (toOnePhase) a[3] = 1;
      chain[chain.length] = a;
      /* если значение уже есть, передаем его наблюдателям */
      if (self.results) _fire(self);
      return self
    }
  };
  /**
   * @class
   * конструктор обьекта работы с асинхронными значениями.
   */
  djp.deferred = function(){
    var self = this;
    if (self == djp){
      return new djp.deferred()
    }
    /**
     * цепочка наблюдателей за значением или ошибкой, получаемых асинхронно.<br>
     * [<br>
     *  onResultCallback:Function,<br>
     *  onErrorCallback:Function,<br>
     *  onCancelCallback:Function,<br>
     *  toOnePhase:Boolean<br>
     * ]
     */
    self.chain = [];
    /**
     * ожидаем получение вложенного асинхронного результата.
     */
    self.paused = 0;
    /**
     * хранит значение или ошибку последнего результата асинхронного вызова.<br>
     * [result:Object, isErrorOrCancel:Number]
     */
    self.results = null;
    /**
     * цепочка отработавших в текущей фазе наблюдателй.
     */
    self.nextPhaseChain = [];
  };
  for (k in o) {
    djp.deferred.prototype[k] = o[k]
  }
};